import Image, ImageFont, ImageDraw, random

from django.contrib.gis.gdal.envelope import Envelope
from django.http import HttpResponse

from globalmaptiles import GlobalMercator

class GoogleOverlay():

	gmt = GlobalMercator()
	double_field_name = None
	single_field_name = None
	icon_width = None
	queryset = None
	
	def __init__(self, z, x, y, image=None, queryset=None, hard_limit=1000, icon=None, field=None, fields=None):

		self.hard_limit = hard_limit
		self.zoom = int(z)
		
		self.ox, self.oy = self.gmt.GoogleTile(int(x),int(y),self.zoom)		#convert google XY image blocks to some other kind of image block format
		self.N, self.W, self.S, self.E = self.gmt.TileLatLonBounds(self.ox, self.oy, self.zoom)	#convert this other image block format into lattitude/longitude bound values
		
		###################
		
		if queryset:
			self.queryset = queryset
		
		###################
		
		if icon:
			self.icon(icon)
		
		###################
		
		if not image:
			self.im = Image.new("RGBA", (256,256))
		else:
			self.im = image
		
		###################
		
		if field:
			self.single_field_name = field
			
		elif fields:
			self.double_field_name = fields
			
		###############
		
		self.gx, self.gy = x, y		# save google's x and y coordinate values for debugging
		

			
	def icon(self, path):
		"""sets the icon, and automatically sets the width of the icon.
		"""
		self.icon = Image.open(path)
		self.icon_width = self.icon.getbbox()[2]
		return self.icon_width
		
	def expand(self):
		"""Extends the bounds by half the width of the icon used so any icons that are placed
		near the edges of any image will not get cut off. This can't be called until an icon has
		been set.
		"""
	
		if not self.icon_width:
			raise ValueError, "Can't expand filter if no icon is set"
			
		##########################################
		
		res = self.gmt.Resolution(self.zoom)		#number of meters in one pixel
		rev = res * self.icon_width / 2			#number of meters for half an icon

		e_lat, e_long = self.gmt.MetersToLatLon(rev, rev)	#expantion in lat and long degrees.
		
		return e_lat, e_long
			
	def get_filter(self, expand=True):
		"""Creates a filter with the bounds being the lattitude/longitude of the image.
		expand=True will expand the bounds one half the icon's width. An icon mut be
		set for this feature to work
		"""
		
		if expand:
			e_lat, e_long = self.expand()
		else:
			e_lat, e_long = 0,0

		ex_W = self.W - e_lat
		ex_E = self.E + e_lat
		ex_S = self.S + e_long
		ex_N = self.N - e_long
		
		if self.single_field_name:
			bounds = Envelope( (ex_W, ex_N, ex_E, ex_S) )
			arg = { self.single_field_name + "__intersects": str(bounds.wkt) }
			
			
		else:
			lat = self.double_field_name[0]
			lng = self.double_field_name[1]
			
			arg = { lat + "__lte": ex_N,
				lat + "__gte": ex_S, 
				lng + "__lte": ex_E,
				lng + "__gte": ex_W,
			      }

		return arg
		
	def shuffle(self):
		"""Shuffles the points so they are rendered in no particular order, helps
		 to minimize odd overlapping effects"""
		
		self.queryset = self.queryset.order_by('?')
		
	def put_points(self):
		"""Evaluates the queryset, goes through each point, calculates the correct
		coordinate for PIL, and then places the points onto the image"""
	
		for base in self.geobases:
		
			if self.single_field_name:
				lat = getattr(base, self.single_field_name).y
				lng = getattr(base, self.single_field_name).x
			else:
				lat = getattr(base, self.single_field_name[0])
				lng = getattr(base, self.single_field_name[1])
			
			meters = self.gmt.LatLonToMeters(lat,lng)				#meters from (0,0) to the point
			pixs =   self.gmt.MetersToPixels(meters[0], meters[1], self.zoom)		#pixels from (0,0) to the point
		
			tx = pixs[0] - (256 * self.ox)						#pixels within this 256x256 image
			ty = pixs[1] - (256 * self.oy)
		
			fx = tx
			fy = 256-ty
		
			x = int(fx - (self.icon_width / 2))		#adjust so the icon is at the center of the point
			y = int(fy - (self.icon_width / 2))
			
			self.im.paste(self.icon, (x, y), self.icon)
		
	def output(self, debug=False, expand=True, just_queryset=False, shuffle=True):
		""" debug=True will print out debug messages onto the image, expand=False will not expand the
		query bounds, and just_queryset=True will return the queryset, instead of the image.
		"""
		
		if not self.queryset or not self.icon_width:			#if queryset is empty or not present or no icon is set, return original image
			
			if debug:
				self.geobases = "empty"
				self.queryset = "empty"
				self.debug_messages()
				
			return self.im
			
		####################
		
		if shuffle:
			self.shuffle()
			
		####################
		
		args = self.get_filter(expand=expand)
		self.geobases = self.queryset.filter(**args)[:self.hard_limit]
		
		if just_queryset:
			return self.geobases
		
		####################
		
		if debug:
			self.debug_messages()
		
		####################
		
		if self.geobases.count() < 1:		# if there are no geobases, just return the unmodified original image
			return self.im
				
		self.put_points()
		
		return self.im
		
	def debug_messages(self):
		"""Prints debug messages and a red bounding box onto the outputted image"""
		
		font = ImageFont.load_default()
		draw = ImageDraw.Draw(self.im)
		
		draw.line( [(0,0),(0,255)], fill='red', width=1)
		draw.line( [(0,255),(255,255)], fill='red', width=1)

		draw.text((10, 150), "zoom=" + str(self.zoom) + " x=" + str(self.gx) + ", y=" + str(self.gy), font=font, fill='black')
		draw.text((10, 230), "inside= " + str(self.geobases.count()), font=font, fill='black')
		draw.text((10, 210), "total = " + str(self.queryset.count()), font=font, fill='black')

